<!DOCTYPE html>
<html>
<head>
<title>This test checks the basic functionality of NodeIterator.</title>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<link rel="stylesheet" href="../../../resources/testharness.css">
</head>
<body>
<script>
function createSampleDOM()
{
    // Tree order: #a -> "b" -> #c -> #d -> "e" -> #f -> "g" -> <!--h--> -> "i" -> <!--j-->.
    var div = document.createElement('div');
    div.id = 'a';
    div.innerHTML = 'b<div id="c"><div id="d">e<span id="f">g<!--h--></span>i</div><!--j--></div>';
    return div;
}

function check_iterator(iterator, root)
{
    assert_equals(iterator.toString(), '[object NodeIterator]');
    assert_equals(iterator.root, root);
    assert_equals(iterator.referenceNode, root);
    assert_equals(iterator.pointerBeforeReferenceNode, true);
    assert_equals(iterator.whatToShow, 0xFFFFFFFF);
    assert_equals(iterator.filter, null);
    assert_readonly(iterator, 'root');
    assert_readonly(iterator, 'referenceNode');
    assert_readonly(iterator, 'pointerBeforeReferenceNode');
    assert_readonly(iterator, 'whatToShow');
    assert_readonly(iterator, 'filter');
    assert_idl_attribute(iterator, 'nextNode');
    assert_idl_attribute(iterator, 'previousNode');
    assert_idl_attribute(iterator, 'detach');
}

test(function ()
{
    var root = createSampleDOM();
    var iterator = document.createNodeIterator(root);
    check_iterator(iterator, root);
}, 'Construct a NodeIterator by document.createNodeIterator(root).');

test(function ()
{
    var root = createSampleDOM();
    var iterator = document.createNodeIterator(root, undefined, undefined);
    check_iterator(iterator, root);
}, 'Construct a NodeIterator by document.createNodeIterator(root, undefined, undefined).');

test(function ()
{
    assert_throws_js(TypeError, function () { document.createNodeIterator(); });
    assert_throws_js(TypeError, function () { document.createNodeIterator(null); });
    assert_throws_js(TypeError, function () { document.createNodeIterator(undefined); });
    assert_throws_js(TypeError, function () { document.createNodeIterator(new Object()); });
    assert_throws_js(TypeError, function () { document.createNodeIterator(1); });
}, 'Give an invalid root node to document.createNodeIterator().');

// |expected| should be an object indicating the expected type of node.
function assert_node(actual, expected)
{
    assert_true(actual instanceof expected.type,
                'Node type mismatch: actual = ' + actual.nodeType + ', expected = ' + expected.nodeType);
    if (typeof(expected.id) !== 'undefined')
        assert_equals(actual.id, expected.id);
    if (typeof(expected.nodeValue) !== 'undefined')
        assert_equals(actual.nodeValue, expected.nodeValue);
}

// |expectedNodes| should be an array of objects that can be passed to |assert_node|.
function testIteratorForwardAndBackward(iterator, expectedNodes)
{
    assert_equals(iterator.referenceNode, iterator.root);
    assert_equals(iterator.pointerBeforeReferenceNode, true);

    // Going forward.
    var index = 0;
    var node;
    while (node = iterator.nextNode()) {
        assert_node(node, expectedNodes[index]);
        assert_node(iterator.referenceNode, expectedNodes[index]);
        assert_equals(iterator.pointerBeforeReferenceNode, false);
        ++index;
    }

    assert_equals(index, expectedNodes.length);
    assert_equals(node, null);
    assert_node(iterator.referenceNode, expectedNodes[expectedNodes.length - 1]);
    assert_equals(iterator.pointerBeforeReferenceNode, false);

    // Going backward.
    --index;
    while (node = iterator.previousNode()) {
        assert_node(node, expectedNodes[index]);
        assert_node(iterator.referenceNode, expectedNodes[index]);
        assert_equals(iterator.pointerBeforeReferenceNode, true);
        --index;
    }

    assert_equals(index, -1);
    assert_equals(node, null);
    assert_node(iterator.referenceNode, expectedNodes[0]);
    assert_equals(iterator.pointerBeforeReferenceNode, true);
}

var expectedAll = [
    { type: Element, id: 'a' },
    { type: Text, nodeValue: 'b' },
    { type: Element, id: 'c' },
    { type: Element, id: 'd' },
    { type: Text, nodeValue: 'e' },
    { type: Element, id: 'f' },
    { type: Text, nodeValue: 'g' },
    { type: Comment, nodeValue: 'h' },
    { type: Text, nodeValue: 'i' },
    { type: Comment, nodeValue: 'j' },
];

test(function ()
{
    var iterator = document.createNodeIterator(createSampleDOM());
    testIteratorForwardAndBackward(iterator, expectedAll);
}, 'Iterate over all nodes forward then backward.');

test(function ()
{
    var expected = [
        { type: Element, id: 'a' },
        { type: Element, id: 'c' },
        { type: Element, id: 'd' },
        { type: Element, id: 'f' },
    ];
    var iterator = document.createNodeIterator(createSampleDOM(), NodeFilter.SHOW_ELEMENT);
    testIteratorForwardAndBackward(iterator, expected);
}, 'Iterate over all elements forward then backward.');

test(function ()
{
    var expected = [
        { type: Text, nodeValue: 'b' },
        { type: Text, nodeValue: 'e' },
        { type: Text, nodeValue: 'g' },
        { type: Text, nodeValue: 'i' },
    ];
    var iterator = document.createNodeIterator(createSampleDOM(), NodeFilter.SHOW_TEXT);
    testIteratorForwardAndBackward(iterator, expected);
}, 'Iterate over all text nodes forward then backward.');

test(function ()
{
    var expected = [
        { type: Comment, nodeValue: 'h' },
        { type: Comment, nodeValue: 'j' },
    ];
    var iterator = document.createNodeIterator(createSampleDOM(), NodeFilter.SHOW_COMMENT);
    testIteratorForwardAndBackward(iterator, expected);
}, 'Iterate over all comment nodes forward then backward.');

test(function ()
{
    var iterator = document.createNodeIterator(createSampleDOM(), 0);
    assert_equals(iterator.referenceNode, iterator.root);
    assert_equals(iterator.pointerBeforeReferenceNode, true);

    assert_equals(iterator.nextNode(), null);
    assert_equals(iterator.referenceNode, iterator.root);
    assert_equals(iterator.pointerBeforeReferenceNode, true);

    assert_equals(iterator.previousNode(), null);
    assert_equals(iterator.referenceNode, iterator.root);
    assert_equals(iterator.pointerBeforeReferenceNode, true);
}, 'Test the behavior of NodeIterator when no nodes match with the given filter.');

test(function ()
{
    var expected = [
        { type: Text, nodeValue: 'e' },
        { type: Element, id: 'f' },
        { type: Comment, nodeValue: 'j' },
    ];
    var filter = function (node) {
        if (node.nodeType === Node.ELEMENT_NODE && node.id === 'f' ||
            node.nodeType === Node.TEXT_NODE && node.nodeValue === 'e' ||
            node.nodeType === Node.COMMENT_NODE && node.nodeValue === 'j') {
            return NodeFilter.FILTER_ACCEPT;
        }
        return NodeFilter.FILTER_REJECT;
    };
    var iterator = document.createNodeIterator(createSampleDOM(), NodeFilter.SHOW_ALL, filter);
    testIteratorForwardAndBackward(iterator, expected);
}, 'Test the behavior of NodeIterator when NodeFilter is specified.');

test(function() {
    var nodeIterator = document.createNodeIterator(document.body, 42, null);
    assert_equals(nodeIterator.root, document.body);
    assert_equals(nodeIterator.referenceNode, document.body);
    assert_equals(nodeIterator.whatToShow, 42);
    assert_equals(nodeIterator.filter, null);
}, "Optional arguments to createNodeIterator should be optional (3 passed, null).");
</script>
</body>
</html>
